package ru.alt22.visor.php.core.codegen;

import java.util.ArrayList;

import org.eclipse.core.resources.IProject;
import org.eclipse.php.internal.core.phpModel.PHPModelUtil;
import org.eclipse.php.internal.core.phpModel.parser.PHPWorkspaceModelManager;
import org.eclipse.php.internal.core.phpModel.phpElementData.CodeData;
import org.eclipse.php.internal.core.phpModel.phpElementData.PHPClassData;
import org.eclipse.php.internal.core.phpModel.phpElementData.PHPFunctionData;
import org.eclipse.php.internal.core.phpModel.phpElementData.PHPModifier;
import org.eclipse.php.internal.core.phpModel.phpElementData.PHPClassData.PHPInterfaceNameData;
import org.eclipse.php.internal.core.phpModel.phpElementData.PHPFunctionData.PHPFunctionParameter;

public class PHPTypeBuilder {
	public final static String CLASS_EXTENSION = ".php";
	public final static String CLASS_CONSTRUCTOR = "__construct";
	public final static String CLASS_DESCRTUCTOR = "__destruct";

	public static PHPClass buildClass(IProject project, String name, String superclass, String[] interfaces) {
		PHPClass result = new PHPClass();
		return result;
	}

	public static PHPInterface buildInterface(IProject project, String name, String[] interfaces) {
		return null;
	}

	public static PHPClassData findClassByName(IProject project, String className) {
		CodeData[] classes = PHPWorkspaceModelManager.getInstance().getModelForProject(project).getClasses(className);
		if (classes == null || classes.length == 0) {
			return null;
		}
		for (int i = 0; i < classes.length; i++) {
			if (classes[i].getName().compareToIgnoreCase(className) == 0) {
				return (PHPClassData) classes[i];
			}
		}
		return (PHPClassData) classes[0];
	}

	public static boolean isClassExists(IProject project, String className) {
		if (findClassByName(project, className) == null) {
			return false;
		}
		return true;
	}

	public static PHPMethodCollection findUnimplementedMethods(PHPClassData superClass, PHPClassData[] interfaces) {
		PHPMethodCollection result = new PHPMethodCollection();
		PHPMethodCollection implementedMethods = new PHPMethodCollection();
		result.addAll(findAbstractMethods(superClass));
		for (int i = 0; i < interfaces.length; i++) {
			result.addAll(findAbstractMethods(interfaces[i]));
		}
		if (superClass == null) {
			return result;
		}
		implementedMethods.addAll(getImplementedMethods(superClass));
		for (int i = 0; i < implementedMethods.length(); i++) {
			String method = implementedMethods.get(i).getName();
			if (result.contains(method)) {
				result.remove(method);
			}
		}

		return result;
	}

	public static ArrayList<PHPMethod> findAbstractMethods(PHPClassData phpClass) {
		PHPMethodCollection result = new PHPMethodCollection();
		if (phpClass == null) {
			return result.toArrayList();
		}
		result.addAll(getAbstractMethods(phpClass));
		PHPClassData parent = PHPModelUtil.getSuperClass(phpClass);
		if (parent != null) {
			result.addAll(findAbstractMethods(parent));
		}
		PHPInterfaceNameData[] phpInterfaces = phpClass.getInterfacesNamesData();
		for (int i = 0; i < phpInterfaces.length; i++) {
			PHPClassData phpInterface = findClassByName(getProjectFor(phpClass), phpInterfaces[i].getName());
			if (phpInterface == null) {
				continue;
			}
			result.addAll(findAbstractMethods(phpInterface));
		}
		return result.toArrayList();
	}

	public static ArrayList<PHPMethod> getAbstractMethods(PHPClassData phpClass) {
		ArrayList<PHPMethod> result = new ArrayList<PHPMethod>();
		if (phpClass == null) {
			return result;
		}
		PHPFunctionData[] methods = phpClass.getFunctions();
		for (int i = 0; i < methods.length; i++) {
			PHPFunctionData classMethod = methods[i];
			int modifier = ((~PHPModifier.ABSTRACT) & classMethod.getModifiers());
			if (modifier == 0) {
				modifier = PHPModifier.PUBLIC;
			}
			if (!PHPModifier.isInterface(phpClass.getModifiers()) && !PHPModifier.isAbstract(classMethod.getModifiers())) {
				continue;
			}
			PHPMethod newMethod = new PHPMethod(modifier, classMethod.getName());
			addMethodParameters(phpClass, classMethod, newMethod);
			result.add(newMethod);
		}
		return result;
	}

	public static ArrayList<PHPMethod> getImplementedMethods(PHPClassData phpClass) {
		PHPMethodCollection result = new PHPMethodCollection();
		if (phpClass == null) {
			return result.toArrayList();
		}
		if (PHPModifier.isInterface(phpClass.getModifiers())) {
			return result.toArrayList();
		}

		PHPFunctionData[] methods = phpClass.getFunctions();
		for (int i = 0; i < methods.length; i++) {
			PHPFunctionData classMethod = methods[i];
			if (PHPModifier.isAbstract(classMethod.getModifiers())) {
				continue;
			}
			if (isConstructor(phpClass, classMethod) || isDestructor(classMethod)) {
				continue;
			}
			PHPMethod newMethod = new PHPMethod(classMethod.getModifiers(), classMethod.getName());
			addMethodParameters(phpClass, classMethod, newMethod);
			result.add(newMethod);
		}

		PHPClassData superclass = PHPModelUtil.getSuperClass(phpClass);
		if (superclass != null) {
			result.addAll(getImplementedMethods(superclass));
		}
		return result.toArrayList();
	}

	public static IProject getProjectFor(PHPClassData phpClass) {
		return PHPModelUtil.getResource(phpClass).getProject();
	}

	private static PHPFunctionParameter[] addMethodParameters(PHPClassData phpClass, PHPFunctionData phpClassMethod, PHPMethod newMethod) {
		PHPFunctionParameter[] parameters = phpClassMethod.getParameters();
		for (int j = 0; j < parameters.length; j++) {
			PHPFunctionParameter parameter = parameters[j];
			String parameterType = parameter.getClassType();
			if (null != parameterType) {
				if (!isClassExists(getProjectFor(phpClass), parameterType)) {
					parameterType = null;
				}
			}
			newMethod.addParameter(parameter.getName(), parameterType, parameter.getDefaultValue());
		}
		return parameters;
	}

	private static boolean isConstructor(PHPClassData classData, PHPFunctionData method) {
		if (method.getName().compareToIgnoreCase(CLASS_CONSTRUCTOR) == 0) {
			return true;
		}
		if (method.getName().compareToIgnoreCase(classData.getName()) == 0) {
			return true;
		}
		return false;
	}

	private static boolean isDestructor(PHPFunctionData method) {
		if (method.getName().compareToIgnoreCase(CLASS_DESCRTUCTOR) == 0) {
			return true;
		}
		return false;
	}

}